

#include "capabilities.h"
#include "ldac_decode_earbud.h"
#ifndef LIB_PKG
#include "debug.h"
#endif
#include "ldac_dec_priv_lib.h"


#include "platform/pl_assert.h"
#include "a2dp_decode/a2dp_common_decode.h"
#include "op_msg_helpers.h"

#include "patch/patch.h"
#include "ldac_decode_earbud_struct.h"
/****************************************************************************
Private Constant Definitions
*/



/****************************************************************************
Private Type Definitions
*/
typedef struct
{
    /** A2DP_DECODER_PARAMS must be the first parameters always */
    A2DP_DECODER_PARAMS    decoder_data;

    /** The ldac_codec specific data */
    LDAC_DEC_PARAMS codec_data;

    /** Timer ID used to kick operator to ensure data keeps flowing */

    // statistics reporting that can be useful for debugging issues
    LDAC_STATISTICS ldac_statistics_reporting;
} LDAC_DEC_OP_DATA;


/****************************************************************************
Private Constant Declarations
*/
#define LDAC_DECODE_PRIVATE_ID  0xC002 /* CHANGE THIS VALUE TO THAT SELECTED */

/** The stub capability function handler table */
const handler_lookup_struct ldac_decode_handler_table =
{
    ldac_decode_create,           /* OPCMD_CREATE */
    ldac_decode_destroy,          /* OPCMD_DESTROY */
    ldac_decode_start,           /* OPCMD_START */
    base_op_stop,                /* OPCMD_STOP */
    ldac_decode_reset,            /* OPCMD_RESET */
    ldac_decode_connect,         /* OPCMD_CONNECT */
    a2dp_decode_disconnect,      /* OPCMD_DISCONNECT */
    ldac_decode_buffer_details,  /* OPCMD_BUFFER_DETAILS */
    a2dp_decode_get_data_format, /* OPCMD_DATA_FORMAT */
    a2dp_decode_get_sched_info   /* OPCMD_GET_SCHED_INFO */
};

/** ldac decode capability data */
const opmsg_handler_lookup_table_entry ldac_decode_opmsg_obpm_handler_table[] =
{
    {OPMSG_COMMON_ID_GET_CAPABILITY_VERSION, base_op_opmsg_get_capability_version},
    {OPMSG_COMMON_ID_FADEOUT_ENABLE,         a2dp_dec_opmsg_enable_fadeout},
    {OPMSG_COMMON_ID_FADEOUT_DISABLE,        a2dp_dec_opmsg_disable_fadeout},
    {OPMSG_COMMON_ID_SET_CONTROL,            ldac_dec_opmsg_obpm_set_control},
    {OPMSG_COMMON_ID_GET_PARAMS,             ldac_dec_opmsg_obpm_get_params},
    {OPMSG_COMMON_ID_GET_DEFAULTS,           ldac_dec_opmsg_obpm_get_defaults},
    {OPMSG_COMMON_ID_SET_PARAMS,             ldac_dec_opmsg_obpm_set_params},
    {OPMSG_COMMON_ID_GET_STATUS,             ldac_dec_opmsg_obpm_get_status},
    {0, NULL}
};

const CAPABILITY_DATA ldac_decode_earbud_cap_data =
{
    LDAC_DECODE_PRIVATE_ID,
    LDAC_DECODE_VERSION_MAJOR, 1, /* Version information - hi and lo parts */
    1, 2,                           /* Max 1 sink and 2 sources */
    &ldac_decode_handler_table,
    ldac_decode_opmsg_obpm_handler_table,
    ldac_decode_process_data,        /* Data processing function */
    0,                              /* TODO: this would hold processing time information */
    sizeof(LDAC_DEC_OP_DATA)
};

#define CAP_ID_LDAC_DECODER 0x4100
#define CAP_ID_DOWNLOAD_LDAC_DECODER 0x4101

#if !defined(CAPABILITY_DOWNLOAD_BUILD)
MAP_INSTANCE_DATA(CAP_ID_LDAC_DECODER, LDAC_DEC_OP_DATA)
#else
MAP_INSTANCE_DATA(CAP_ID_DOWNLOAD_LDAC_DECODER, LDAC_DEC_OP_DATA)
#endif /* CAPABILITY_DOWNLOAD_BUILD */



// defined in private library
extern malloc_t_entry ldac_dec_malloc_table[];

unsigned char left_out_pcm[1024];
unsigned char right_out_pcm[1024];
long kick_count = 0;

/***************************************************************************
Private Function Declarations
*/
static LDAC_DEC_OP_DATA *get_instance_data(OPERATOR_DATA *op_data)
{
    return (LDAC_DEC_OP_DATA *) base_op_get_instance_data(op_data);
}


/**
 * \brief timer handler to allow internal self-kick
 * This prevents a stall when buffers fill up
 *
 * \param timer_data Pointer to the operator instance data.
 */
void ldac_decode_timer_task(void *timer_data)
{
    OPERATOR_DATA *op_data = (OPERATOR_DATA*) timer_data;
    /* Raise a bg int to process */
    opmgr_kick_operator(op_data);
}

/**
 * \brief Frees up any data that has been allocated by the instance of the
 * ldac_decode capability.
 *
 * \param op_data Pointer to the operator instance data.
 */
static void free_data(OPERATOR_DATA *op_data)
{
    LDAC_DEC_OP_DATA *ldac_data = get_instance_data(op_data);

    if( ldac_data->codec_data.cb_stream != NULL )
      cbuffer_destroy(ldac_data->codec_data.cb_stream);
    if( ldac_data->codec_data.RxBuf.carried != NULL )
      cbuffer_destroy(ldac_data->codec_data.RxBuf.carried);
    if( ldac_data->codec_data.RxBuf.clone != NULL )
      cbuffer_destroy_struct(ldac_data->codec_data.RxBuf.clone);
    if( ldac_data->codec_data.RxBuf.main != NULL )
      cbuffer_destroy(ldac_data->codec_data.RxBuf.main);

    /* Free all the scratch memory we reserved */
    scratch_deregister();

    /* free the shared codec data */
    mem_table_free((void *)(ldac_data->codec_data.hData),ldac_malloc_table3, LDAC_TABLE_LENGTH);

}

/**
 * \brief Frees up any data that has been allocated by the instnace of the
 * ldac_decode capability and sets the response field to failed.
 *
 * \param op_data Pointer to the operator instance data.
 * \param response Pointer to the response message to give a failed status
 */
static void free_data_and_fail(OPERATOR_DATA *op_data, void **response)
{
    /* Free the data and then override the response message status to fail */
    LDAC_DEC_OP_DATA *ldac_dec_op_data = get_instance_data(op_data);
    if( ldac_dec_op_data->codec_data.cb_stream != NULL )
      cbuffer_destroy(ldac_dec_op_data->codec_data.cb_stream);
    if( ldac_dec_op_data->codec_data.RxBuf.carried != NULL )
      cbuffer_destroy(ldac_dec_op_data->codec_data.RxBuf.carried);
    if( ldac_dec_op_data->codec_data.RxBuf.main != NULL )
      cbuffer_destroy(ldac_dec_op_data->codec_data.RxBuf.main);
    if( ldac_dec_op_data->codec_data.RxBuf.clone != NULL )
      cbuffer_destroy_struct(ldac_dec_op_data->codec_data.RxBuf.clone);
    
    free_data(op_data);
    base_op_change_response_status(response, STATUS_CMD_FAILED);
}

/**
 * \brief Allocates the ldac_decode specific capability memory and initialises
 * the decoder.
 *
 * \param op_data Pointer to the operator instance data.
 * \param message_data Pointer to the create request message
 * \param response_id Location to write the response message id
 * \param response_data Location to write a pointer to the response message
 *
 * \return Whether the response_data field has been populated with a valid
 * response
 */

bool ldac_decode_create(OPERATOR_DATA *op_data, void *message_data,
                                unsigned *response_id, void **response_data)
{ 
    LDAC_DEC_OP_DATA *ldac_data = get_instance_data(op_data);

    
    /* call base_op create, which also allocates and fills response message */
    if (!base_op_create(op_data, message_data, response_id, response_data))
    {
        return FALSE;
    }

    ldac_data->codec_data.cb_stream = NULL;
    ldac_data->codec_data.RxBuf.carried = NULL;
    ldac_data->codec_data.RxBuf.main = NULL;
    ldac_data->codec_data.RxBuf.clone = NULL;
    
    /* create a new buffer */
    ldac_data->codec_data.cb_stream = cbuffer_create_with_malloc((MAX_LDAC_FRAME_SIZE+3)/4+1, BUF_DESC_LOCAL_MMU_BUFFER);
    if (ldac_data->codec_data.cb_stream == NULL)
    {
        L4_DBG_MSG("LDAC cbuffer_create_with_malloc fail-3");
        return FALSE;
    }

    ldac_data->codec_data.RxBuf.carried = cbuffer_create_with_malloc(LDAC_RXBUF_CARRIED_SIZE, BUF_DESC_LOCAL_MMU_BUFFER);
    if (ldac_data->codec_data.RxBuf.carried == NULL)
    {
        L4_DBG_MSG("LDAC cbuffer_create_with_malloc fail-5");
        return FALSE;
    }
    /* create a new buffer */
    ldac_data->codec_data.RxBuf.main = cbuffer_create_with_malloc((LDAC_RXBUF_MAIN_SIZE), BUF_DESC_LOCAL_MMU_BUFFER);

    if (ldac_data->codec_data.RxBuf.main == NULL)
    {
        L4_DBG_MSG("LDAC cbuffer_create_with_malloc fail-6");
        return FALSE;
    }

    // clone codec_data.RxBuf.main
    ldac_data->codec_data.RxBuf.clone = cbuffer_create(ldac_data->codec_data.RxBuf.main->base_addr,
                                                cbuffer_get_size_in_words(ldac_data->codec_data.RxBuf.main),
                                                ldac_data->codec_data.RxBuf.main->descriptor);

    if (ldac_data->codec_data.RxBuf.clone == NULL)
    {
        L4_DBG_MSG("LDAC cbuffer_create_with_malloc fail-7");
        return FALSE;
    }
    
    ldac_data->codec_data.hData = (HANDLE_LDAC_BT)g_ldacbt_handle;

    /* Now reserve the scratch memory */
    if (scratch_register())
    {
        if (mem_table_scratch_tbl_reserve(&decoder_scratch_table))
        {
            /* Successfully allocated everything! */
            /* Stage 2 populate the DECODER structure */
            /* Tell the codec structure where to find the ldac codec data */
            ldac_data->decoder_data.codec.decoder_data_object = &(ldac_data->codec_data);
            /*clear data*/
            ldac_data->codec_data.remain_byte=0;
            ldac_data->codec_data.remain_byte_flg=0;
             return TRUE;
        }
        /* Fail free all the scratch memory we reserved */
        scratch_deregister();
    }
    /* Clear up all the allocated memory. */
    free_data_and_fail(op_data, response_data);
    return TRUE;
}

/**
 * \brief Reports the buffer requirements of the requested capability terminal
 *
 * \param op_data Pointer to the operator instance data.
 * \param message_data Pointer to the buffer size request message
 * \param response_id Location to write the response message id
 * \param response_data Location to write a pointer to the response message
 *
 * \return Whether the response_data field has been populated with a valid
 * response
 */

 bool ldac_decode_buffer_details(OPERATOR_DATA *op_data, void *message_data,
                                    unsigned *response_id, void **response_data)
{
    return a2dp_decode_buffer_details_core(op_data, message_data,response_id, response_data,A2DP_DECODE_INPUT_BUFFER_SIZE,LDAC_OUTPUT_BUFF_LENGTH, LDAC_OUTPUT_BUFF_LENGTH);
}


/**
 * \brief Deallocates the ldac_decode specific capability memory.
 *
 * \param op_data Pointer to the operator instance data.
 * \param message_data Pointer to the destroy request message
 * \param response_id Location to write the response message id
 * \param response_data Location to write a pointer to the response message
 *
 * \return Whether the response_data field has been populated with a valid
 * response
 */
bool ldac_decode_destroy(OPERATOR_DATA *op_data, void *message_data,
                                    unsigned *response_id, void **response_data)
{
    L2_DBG_MSG("LDAC decode: ldac_decode_destroy");

    /* check that we are not trying to destroy a running operator */
    if (opmgr_op_is_running(op_data))
    {
        /* We can't destroy a running operator. */
        return base_op_build_std_response_ex(op_data, STATUS_CMD_FAILED, response_data);
    }
    else if(base_op_destroy(op_data, message_data, response_id, response_data))
    {

        /* Clear up the ldac_decode specific work and then let base_op do
         * the grunt work. */
        free_data(op_data);
        return TRUE;
    }
    return FALSE;
}

/**
 * \brief Starts the ldac_decode capability so decoding will be attempted on a
 * kick.
 *
 * \param op_data Pointer to the operator instance data.
 * \param message_data Pointer to the start request message
 * \param response_id Location to write the response message id
 * \param response_data Location to write a pointer to the response message
 *
 * \return Whether the response_data field has been populated with a valid
 * response
 */
bool ldac_decode_start(OPERATOR_DATA *op_data, void *message_data,
                                    unsigned *response_id, void **response_data)
{
    LDAC_DEC_OP_DATA *ldac_dec_op_data = get_instance_data(op_data);

    ldac_decoder_initiliase(&ldac_dec_op_data->codec_data);

    return a2dp_decode_start(op_data, message_data, response_id, response_data);
}


/**
 * \brief Resets the ldac_decode capability.
 *
 * \param op_data Pointer to the operator instance data.
 * \param message_data Pointer to the reset request message
 * \param response_id Location to write the response message id
 * \param response_data Location to write a pointer to the response message
 *
 * \return Whether the response_data field has been populated with a valid
 * response
 */
bool ldac_decode_reset(OPERATOR_DATA *op_data, void *message_data,
                                    unsigned *response_id, void **response_data)
{
    if (!base_op_reset(op_data, message_data, response_id, response_data))
    {
        return FALSE;
    }
    return TRUE;
}


 /**
 * \brief Connects a capability terminal to a buffer.
 *
 * \param op_data Pointer to the operator instance data.
 * \param message_data Pointer to the connect request message
 * \param response_id Location to write the response message id
 * \param response_data Location to write a pointer to the response message
 *
 * \return Whether the response_data field has been populated with a valid
 * response
 */
 
bool ldac_decode_connect(OPERATOR_DATA *op_data, void *message_data,
                                    unsigned *response_id, void **response_data)
{
    return a2dp_decode_connect(op_data, message_data,response_id, response_data);
}


/**
 * \brief Reports the buffer requirements of the requested capability terminal
 *
 * \param op_data Pointer to the operator instance data.
 * \param touched Structure to return the terminals which this operator wants kicked
 */

void ldac_decode_process_data(OPERATOR_DATA *op_data, TOUCHED_TERMINALS *touched)
{
    LDAC_DEC_OP_DATA *ldac_data;
    int *left_write_ptr, *right_write_ptr;
    int nFrmRx = 0;
    unsigned out_length;
    unsigned nFrmToDec = 0;
    int no_of_frames_processed;
    metadata_tag *mtag_frames;
    bool RepeatLoop = TRUE;

    patch_fn(ldac_decode_process_data);
    ldac_data = get_instance_data(op_data);

    left_write_ptr = ldac_data->decoder_data.codec.out_left_buffer->write_ptr;
    right_write_ptr = ldac_data->decoder_data.codec.out_right_buffer->write_ptr;

    //debug data
    kick_count++;

    /* Check the input hasn't gone away, if it has then nothing we can do. It's
     * a radio link so this can happen */
    if (   (NULL == ldac_data->decoder_data.codec.in_buffer)
        || (NULL == ldac_data->codec_data.RxBuf.main)
        || (NULL == ldac_data->codec_data.RxBuf.clone)
        || (NULL == ldac_data->codec_data.cb_stream))
    {
        L2_DBG_MSG4("LDAC decode: *** error NULL buffer *** %x %x %x %x ",ldac_data->decoder_data.codec.in_buffer
                    ,ldac_data->codec_data.RxBuf.main
                    ,ldac_data->codec_data.RxBuf.clone
                    ,ldac_data->codec_data.cb_stream);

        /* One option is to produce silence in this case.*/
        return;
    }

    /*  check whether input buffer is full */
    if (!cbuffer_calc_amount_space_ex(ldac_data->decoder_data.codec.in_buffer))
    {
        // statistics recording
        ldac_data->ldac_statistics_reporting.input_buffer_full++;
        L2_DBG_MSG1("LDAC decode: codec_in is full, kick_count = %d", kick_count);
    }
    // check for empty input buffer
    else if(!cbuffer_calc_amount_data_ex(ldac_data->decoder_data.codec.in_buffer))
    {
        // statistics recording
        ldac_data->ldac_statistics_reporting.input_buffer_empty++;
    }

    // reset no of frames to decode count
    nFrmToDec = 0;

    // copy any new data from codec in buffer to cb_main intermediate buffer along with the corresponding metadata frames, this
    // is necessary to allow a larger buffer of frames to be maintained to satisfy the requirement that streaming is not
    // started until 64 frames of audio have been stored
    if(ldac_decoder_stores_rx_data(ldac_data->decoder_data.codec.in_buffer, &ldac_data->codec_data, &nFrmRx))
    {
        L2_DBG_MSG1("ldac_decoder_store_rx_data fail kick_count = %d",kick_count);
    }
    else
    {
        L4_DBG_MSG1("ldac_decoder_store_rx_data copied %d frames from codec_in to intermediate buffer",nFrmRx);
    }

    L4_DBG_MSG1("ldac no_of_frames_buffered = %d",ldac_decoder_enough_frames_to_decode( &ldac_data->codec_data ));

    // try to copy as much data as will fit in the output buffer
    RepeatLoop = TRUE;

    // repeat until output buffer full or run out of valid input data
    while(RepeatLoop == TRUE)
    {
        // ensure there is data frames to process
        if(ldac_decoder_enough_frames_to_decode( &ldac_data->codec_data ) > 0)
        {
            // try to get next metadata tag to work out how many frames are available for copying
            mtag_frames = buff_metadata_peek(ldac_data->codec_data.RxBuf.main);

            // if tag is present in buffer, data is available to process
            if(mtag_frames !=NULL)
            {
                unsigned *meta_data_no_of_frames;

                // get the number of audio frames from the metadata tag private data
                if (buff_metadata_find_private_data(mtag_frames, META_PRIV_KEY_USER_DATA, &out_length,
                                                                    (void **)&meta_data_no_of_frames))
                {
                    nFrmToDec = *meta_data_no_of_frames;

                    L4_DBG_MSG4("ldac_process_data  no_of_frames %d data_size %d output_buf_space %d frames left = %d ",nFrmToDec,nFrmToDec * ldac_data->decoder_data.codec.num_output_samples,cbuffer_calc_amount_space_ex(ldac_data->decoder_data.codec.out_left_buffer),ldac_decoder_enough_frames_to_decode( &ldac_data->codec_data ));

                    // ensure data frames to copy and enough space in the output buffer for the number of frames to process
                    if((nFrmToDec>0) && (cbuffer_calc_amount_space_ex(ldac_data->decoder_data.codec.out_left_buffer) > (nFrmToDec * ldac_data->decoder_data.codec.num_output_samples * OCTETS_PER_SAMPLE)))
                    {
                        unsigned b4idx, afteridx;

                        /* reset decode status */
                        ldac_data->decoder_data.codec.mode = CODEC_SUCCESS;

                        // store number of frames processed for metadata handling
                        no_of_frames_processed = 0;

                        // loop around the decoder call f or the number of frames available
                        for(int i=0;i<nFrmToDec;i++)
                        {
                            // reset pcm output data write pointers
                            ldac_data->codec_data.p_pcm_out_0 = &left_out_pcm[0];
                            ldac_data->codec_data.p_pcm_out_1 = &right_out_pcm[0];

                            // decode nFrmToDec number of frames
                            ldac_data->decoder_data.codec.mode =
                                ldac_decoder_decode_frames(&ldac_data->codec_data, ldac_data->codec_data.p_pcm_out_0, ldac_data->codec_data.p_pcm_out_1);

                            // ensure no errors occurred
                            if (ldac_data->decoder_data.codec.mode != CODEC_SUCCESS)
                            {
                                    L2_DBG_MSG1("LDAC decode error mode=%d",ldac_data->decoder_data.codec.mode);
                                    ldac_decoder_initiliase(&ldac_data->codec_data);
                                    // statistics recording
                                    ldac_data->ldac_statistics_reporting.decode_data_error++;
                                    break;
                            }

                            // update number of output samples copied
                            ldac_data->decoder_data.codec.num_output_samples = ldac_data->codec_data.num_output_samples;
                            // and frame size of this group of frames
                            ldac_data->decoder_data.codec.num_input_octets_consumed = ldac_data->codec_data.frame_size;

                            // debug use only
                            L4_DBG_MSG4("LDAC decode success = nFrmToDec = %d num_output_samples=%d frame_size=%d ttp = %d", nFrmToDec, ldac_data->decoder_data.codec.num_output_samples, ldac_data->codec_data.frame_size, mtag_frames->timestamp);

                            // copy the decoded data frame to the output buffer
                            if (ldac_data->decoder_data.codec.num_output_samples > 0)
                            {

                                // copy decoded audio stored in linear pcm buffer to the cap output cbuffers
                                cbuffer_write(ldac_data->decoder_data.codec.out_left_buffer, (int*)ldac_data->codec_data.p_pcm_out_0, ldac_data->decoder_data.codec.num_output_samples);
                                cbuffer_write(ldac_data->decoder_data.codec.out_right_buffer, (int*)ldac_data->codec_data.p_pcm_out_1, ldac_data->decoder_data.codec.num_output_samples);

                                // one frame of data copied
                                no_of_frames_processed++;

                                L4_DBG_MSG3("LDAC decode write output : left-right = %d op level words = %d ip level octets = %d",ldac_data->decoder_data.codec.num_output_samples
                                                                                                                    , cbuffer_calc_amount_data_ex(ldac_data->decoder_data.codec.out_left_buffer)
                                                                                                                    , (cbuffer_calc_amount_data_ex(ldac_data->decoder_data.codec.in_buffer)*4));

                            }
                        }

                        // if frames/samples were processed, update metadata tag
                        if(no_of_frames_processed>0)
                        {
                            //metadata_tag *mtag_new = buff_metadata_new_tag();

                            /* Source 0 is always touched */
                            tCbuffer fadeout_buffer;

                            L4_DBG_MSG4("LDAC decode success frames processed = %d frame_size=%d output samples = %d kick_count = %d", no_of_frames_processed, ldac_data->codec_data.frame_size, ((no_of_frames_processed * ldac_data->decoder_data.codec.num_output_samples) * OCTETS_PER_SAMPLE), kick_count);

                           // when the last frame has been processed and copied, remove the metadata tag from the intermediate buffer and
                           // propagate it to the output buffers

                            //mtag_copied = NULL;
                            b4idx = 0;
                            afteridx = 0;
                            tCbuffer *src, *dst;

                            // get pointers to output buffer and metadata buffer
                            src = ldac_data->codec_data.RxBuf.main;
                            dst = ldac_data->decoder_data.metadata_op_buffer;

                            L4_DBG_MSG3("LDAC handle decoded metadata frames=%d input_frame_size=%d rxbuf_avail=%d", no_of_frames_processed, ldac_data->codec_data.frame_size,buff_metadata_available_octets(src));

                            // remove metadata tag from intermediate buffer, frame size * no of frames + 1 byte for frame count, frame count is not used after this point
                            mtag_frames = buff_metadata_remove(src, mtag_frames->length, &b4idx, &afteridx);
                            if(!mtag_frames)
                            {
                                L2_DBG_MSG("LDAC *** buff_metadata_remove failed *** ");
                            }

                            // propagate tag to output buffer
                            if ((buff_has_metadata(dst))&&(mtag_frames != NULL))
                            {
                                unsigned output_octets, after_octets;

                                /* decoded output has the frame lengths dependant upon sample rate being used */
                                output_octets = after_octets = (no_of_frames_processed * ldac_data->decoder_data.codec.num_output_samples) * OCTETS_PER_SAMPLE;

                                // resize tag as data is now decoded depending upon sample rate, 96k = 256 frame, 48k = 128 frame
                                mtag_frames->length = output_octets;

                                L4_DBG_MSG3("LDAC handle decoded metadata frames=%d input_frame_size=%d output_octets=%d", no_of_frames_processed, ldac_data->codec_data.frame_size,output_octets);

                                // vaidity check on current tag
                                if(mtag_frames != NULL)
                                {
                                    // append updated tag to output buffer
                                    buff_metadata_append(dst, mtag_frames, 0, after_octets);
                                    L4_DBG_MSG1("LDAC add outptut tag - tag_length =%d", mtag_frames->length);
                                }
                                // no valid tag, can occur on startup
                                else
                                {
                                    if(buff_metadata_enable(dst)==NULL)
                                    {
                                        L2_DBG_MSG("LDAC cb_main buff_metadata_enable failed");
                                    }
                                    else
                                    {
                                        /* Continue adding data to the last tag.*/
                                        buff_metadata_append(dst, mtag_frames, output_octets, 0);
                                        L4_DBG_MSG1("LDAC add outptut tag 2 - tag_length =%d", mtag_frames->length);
                                    }
                                }
                            }
                            // no metadata support in output buffer, delete tag to prevent tags building up and stalling chain
                            else
                            {
                                // delete tag if valid
                                if(mtag_frames != NULL)
                                    buff_metadata_delete_tag(mtag_frames,FALSE);

                                // ensure buffer has metadata enabled before trying to enable
                                if((dst!=NULL)&&(!buff_has_metadata(dst)))
                                {
                                    buff_metadata_enable(dst);
                                }
                            }

                            /* Is fadeout enabled? if yes, do it on the new output data */
                            if ( (ldac_data->decoder_data.left_fadeout.fadeout_state != NOT_RUNNING_STATE))
                            {
                                /* If it is enabled it is enabled on both channels. Call it on left and
                                 * right. They should both fadeout at the same rate so if one says
                                 * finished then they both should be and we tell the host. */

                                /* A small optimisation here, if there is only a mono output connected
                                 * then we only need to call fade out on the left channel, which is
                                 * why we tested the left state above. If left is fading out it is
                                 * definitely connected so we always fade it out. By fading out the
                                 * right channel first (iff it is connected) we don't need to think
                                 * about it's response or when to send the fade out done message.
                                 */
                                if (ldac_data->decoder_data.codec.out_right_buffer != NULL)
                                {
                                    fadeout_buffer = *ldac_data->decoder_data.codec.out_right_buffer;
                                    fadeout_buffer.read_ptr = right_write_ptr;
                                    mono_cbuffer_fadeout(&fadeout_buffer,
                                                        ldac_data->decoder_data.codec.num_output_samples, &(ldac_data->decoder_data.right_fadeout));
                                }
                                fadeout_buffer = *ldac_data->decoder_data.codec.out_left_buffer;
                                fadeout_buffer.read_ptr = left_write_ptr;
                                if (mono_cbuffer_fadeout(&fadeout_buffer,
                                                        ldac_data->decoder_data.codec.num_output_samples, &(ldac_data->decoder_data.left_fadeout)))
                                {
                                    common_send_simple_unsolicited_message(op_data, OPMSG_REPLY_ID_FADEOUT_DONE);
                                }
                           }

                            // keep data moving if output buffers still have data in them
                            if(cbuffer_calc_amount_data_ex(ldac_data->decoder_data.codec.out_left_buffer)>0)
                                touched->sources = TOUCHED_SOURCE_0;
                            if(cbuffer_calc_amount_data_ex(ldac_data->decoder_data.codec.out_right_buffer)>0)
                                touched->sources = (TOUCHED_SOURCE_0|TOUCHED_SOURCE_1);


                        // if samples were successfully copied, transfer them to the output buffers and update metadata tag
                        }
                        // no data processed
                        else
                        {
                            L2_DBG_MSG("**** Error **** NO LDAC metadata frames processed ");

                            // wait for more data
                            RepeatLoop = FALSE;

                            // remove metadata tag from intermediate buffer, frame size * no of frames + 1 byte for frame count, frame count is not used after this point
                            //mtag_copied = buff_metadata_remove(ldac_data->codec_data.RxBuf.main, mtag_frames->length, &b4idx, &afteridx);
                            if(mtag_frames!=NULL)
                                buff_metadata_delete_tag(mtag_frames,FALSE);

                        }
                    // ensure data frames to copy and enough space in the output buffer for the number of frames to process
                    }
                    else
                    {
                        L4_DBG_MSG2("*** ERROR **** LDAC process data - no space in output buffer - kick %d frames left %d",kick_count,ldac_decoder_enough_frames_to_decode( &ldac_data->codec_data ));
                        // wait for output data to drain down
                        RepeatLoop = FALSE;
                        // keep data moving
                        touched->sources = TOUCHED_SOURCE_0;
                        if(cbuffer_calc_amount_data_ex(ldac_data->decoder_data.codec.out_right_buffer)>0)
                            touched->sources = (TOUCHED_SOURCE_0|TOUCHED_SOURCE_1);
                    }
                // check private data in metadata tag
                }
                // no frames to copy, corrupt metadata tag - TBD
                else
                {
                    L2_DBG_MSG1("*** ERROR **** LDAC process data - no frames in metadatatag",nFrmToDec);
                    // wait for more data
                    RepeatLoop = FALSE;

                    if(cbuffer_calc_amount_data_ex(ldac_data->decoder_data.codec.out_left_buffer)>0)
                        touched->sources = TOUCHED_SOURCE_0;
                    if(cbuffer_calc_amount_data_ex(ldac_data->decoder_data.codec.out_right_buffer)>0)
                        touched->sources = (TOUCHED_SOURCE_0|TOUCHED_SOURCE_1);
                }
            }
        }
        // run out of data in intermediate buffer, pause playback to allow buffer to return to full level,
        // this prevents constant glitching should there be an issue with link bandwidth
        else
        {
            L4_DBG_MSG2("*** ERROR **** LDAC process data - out of data - restart **** kicks = %d, codec in level = %d",kick_count,cbuffer_calc_amount_data_ex(ldac_data->decoder_data.codec.in_buffer));

            // reset playing flag to force the buffers to refill to 64 frames before streaming resumes
            //ldac_data->codec_data.playing=FALSE;
            // statistics recording
            ldac_data->ldac_statistics_reporting.kicks_with_no_data++;
            // wait for more data
            RepeatLoop = FALSE;
        }
    }


     /* If the play pointer is not populated it means that we are connected to RTP
     * decode. If there was not enoguh input kick backwards. In case of an error
     * (CODEC_ERROR or CODEC_FRAME_CORRUPT) kick backwards if there is less than
     * the maximum LDAC frame size (worst case scenario).
     */
    if ((ldac_data->decoder_data.codec.mode == CODEC_NOT_ENOUGH_INPUT_DATA)
       || (((ldac_data->decoder_data.codec.mode == CODEC_ERROR) || (ldac_data->decoder_data.codec.mode == CODEC_FRAME_CORRUPT))
       && (cbuffer_calc_amount_data_ex(ldac_data->codec_data.RxBuf.main) < MAX_LDAC_FRAME_SIZE)
      ) )
    {
        touched->sinks |= TOUCHED_SINK_0;
    }

    // out of data,request more
    if (cbuffer_calc_amount_data_ex(ldac_data->codec_data.RxBuf.main) < MAX_LDAC_FRAME_SIZE)
    {
        touched->sinks |= TOUCHED_SINK_0;
    }

    // keep output data moving
    if(cbuffer_calc_amount_data_ex(ldac_data->decoder_data.codec.out_left_buffer)>0)
        touched->sources = TOUCHED_SOURCE_0;
    if(cbuffer_calc_amount_data_ex(ldac_data->decoder_data.codec.out_right_buffer)>0)
        touched->sources = (TOUCHED_SOURCE_0|TOUCHED_SOURCE_1);


    return;
}

#define LDAC_CTRL_PARAMS_ID_SF 0xA0
#define LDAC_CTRL_PARAMS_ID_CM 0xA1
bool ldac_dec_opmsg_obpm_set_control(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data)
{
    
    LDAC_DEC_OP_DATA *ldac_dec_op_data = get_instance_data(op_data);

    CPS_CONTROL_SOURCE  cntrl_src;
    unsigned  num_controls, i, cntrl_id, cntrl_value; 
    OPMSG_RESULT_STATES result = OPMSG_RESULT_STATES_NORMAL_STATE;

    if (opmgr_op_is_running(op_data))
    {
        return FALSE;
    }

    result = OPMSG_RESULT_STATES_NORMAL_STATE;

    patch_fn(ldac_dec_opmsg_obpm_set_control_patch);
    
    if(!cps_control_setup(message_data, resp_length, resp_data, &num_controls))
    {
       return FALSE;
    }

    for(i=0; i<num_controls; i++)
    {
        cntrl_id = cps_control_get(message_data, i, &cntrl_value, &cntrl_src);

        if(cntrl_id == LDAC_CTRL_PARAMS_ID_SF)
        {
            ldac_dec_op_data->codec_data.sampling_freq = cntrl_value;
        }
        else if(cntrl_id == LDAC_CTRL_PARAMS_ID_CM)
        {
            ldac_dec_op_data->codec_data.channel_mode = cntrl_value;
        }
        else {
            result = OPMSG_RESULT_STATES_INVALID_PARAMETER;
        }
    }

    ldac_dec_op_data->codec_data.frame_length_sample = 128;
    switch (ldac_dec_op_data->codec_data.sampling_freq)
    {
      case 192000:
        ldac_dec_op_data->codec_data.frame_length_sample <<= 1;
      case  96000:
        ldac_dec_op_data->codec_data.frame_length_sample <<= 1;
      case  48000:
        ldac_dec_op_data->codec_data.frame_length_us = 2667;
        break;
      case 176400:
        ldac_dec_op_data->codec_data.frame_length_sample <<= 1;
      case  88200:
        ldac_dec_op_data->codec_data.frame_length_sample <<= 1;
      case  44100:
        ldac_dec_op_data->codec_data.frame_length_us = 2902;
        break;
      default:
        result = OPMSG_RESULT_STATES_INVALID_PARAMETER;
    }

    if( result == OPMSG_RESULT_STATES_NORMAL_STATE ) {
        HANDLE_LDAC_BT hData;
        int cm, nshift, sf, ldac_result;

        
       hData = ldac_dec_op_data->codec_data.hData;
       cm    = ldac_dec_op_data->codec_data.channel_mode;
       ldacBT_get_handle(hData,cm);
        
        
        hData = ldac_dec_op_data->codec_data.hData;
        cm    = ldac_dec_op_data->codec_data.channel_mode;
        sf    = ldac_dec_op_data->codec_data.sampling_freq;
        nshift= 0;
        ldac_result = ldacBT_init_handle_decode( hData, cm, sf, nshift, 0, 0 );
        if(ldac_result){
            result = OPMSG_RESULT_STATES_INVALID_PARAMETER;
            //return FALSE;
        }
    }

    cps_response_set_result(resp_data, result);

    return TRUE;
}


bool ldac_dec_opmsg_obpm_get_params(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data)
{
    return FALSE;
}

bool ldac_dec_opmsg_obpm_get_defaults(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data)
{
  
     return FALSE;
}

bool ldac_dec_opmsg_obpm_set_params(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data)
{
    /* Set the parameter(s). For future proofing, it is using the whole mechanism, although currently there is only one field
     * in opdata structure that is a setable parameter. If later there will be more (ever), must follow contiquously the first field,
     * as commented and instructed in the op data definition. Otherwise consider moving them into a dedicated structure.
     */

    return FALSE;
}

bool ldac_dec_opmsg_obpm_get_status(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data)
{
    LDAC_DEC_OP_DATA * op_extra_data = get_instance_data(op_data);

    unsigned* resp = NULL;
    
    if(!common_obpm_status_helper(message_data,resp_length,resp_data,sizeof(LDAC_DECODE_STATISTICS) ,&resp))
    {
         return FALSE;
    }

    if(resp)  
    {    
            resp = cpsPack2Words(op_extra_data->codec_data.sampling_freq, op_extra_data->codec_data.channel_mode, resp);
            cpsPack1Word(op_extra_data->codec_data.frame_size,resp);
    }
    return TRUE;
}

